#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <list>

#include <ev.h>
#include <json/json.h>
#include "game_log.h"


const char policy_cross_domain[] = "<cross-domain-policy>\
<allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\0";
const char policy_file[] = "<policy-file-request/>";

enum parse_state
{
	PARSE_HEADER = 0,
	PARSE_BODY = 1,
	PARSE_ERR = 2,
	PARSE_CROSS_DOMAIN = 3,
};

#ifndef MAX_BUF_LEN
#   define MAX_BUF_LEN (1024*8)
#endif

#ifndef DEF_BUF_LEN
#   define DEF_BUF_LEN (1024*8)
#endif

struct Buffer {
	char	*data;
	ssize_t len;
	ssize_t pos;

	Buffer(const char *bytes, ssize_t nbytes)
	{
		pos = 0;
		len = nbytes;
		data = new char[nbytes];
		memcpy(data, bytes, nbytes);
	}

	virtual ~Buffer()
	{
		delete[] data;
	}

	char *dpos()
	{
		return data + pos;
	}

	ssize_t nbytes()
	{
		return len - pos;
	}
};

class Player;

class TcpClient {
public:
	std::string         body;
	int 				fd;
	int					is_err;

	int                 is_close;

protected:
	int                 _state;
	ev_io 				_ev_write;
	ev_io 				_ev_read;

	std::list<Buffer*>  _write_q;
	char                _header[sizeof(struct Header)];
	struct Header       *_header_p;
	unsigned int        _cur_head_len;
	unsigned int        _cur_policy_len;

public:
	TcpClient(int fd_in)
		: fd(fd_in)
		, is_err(0)
		, is_close(0)
		, _state(PARSE_HEADER)
		, _header_p((struct Header*)_header)
		, _cur_head_len(0)
		, _cur_policy_len(0)
	{
		_ev_read.data = this;
		ev_io_init(&_ev_read, TcpClient::read_cb, fd, EV_READ);
		ev_io_start(YXJMJ_CONF._loop, &_ev_read);

		ev_io_init(&_ev_write, TcpClient::write_cb, fd, EV_WRITE);
	}
	virtual ~TcpClient()
	{

		ev_io_stop(YXJMJ_CONF._loop, &_ev_read);
		ev_io_stop(YXJMJ_CONF._loop, &_ev_write);
		while (!_write_q.empty())
		{
			//Buffer* buffer = _write_q.front();
			//write(fd, buffer->dpos(), buffer->nbytes());
			delete (_write_q.front());
			_write_q.pop_front();
		}
		close(fd);
	}
	virtual void destroy() = 0;
	virtual void pre_destroy() = 0;
	virtual int handle_data() = 0;
	static void read_cb(struct ev_loop *loop, struct ev_io *w, int revents)
	{
		int ret;
		static char recv_buf[DEF_BUF_LEN];

		TcpClient *self = (TcpClient*)w->data;

		if (self->_state == PARSE_HEADER)
		{
			ret = read(self->fd, &self->_header[self->_cur_head_len],
				sizeof(struct Header) - self->_cur_head_len);
			if (ret < 0)
			{
				if (errno == EAGAIN || errno == EINPROGRESS || errno == EINTR)
				{
					/* redo */
					GAME_LOG.error("read header failed[%s]\n", strerror(errno));
					return;
				}
				GAME_LOG.error("unknow err in read header[%d]\n", self->fd);
				self->destroy();
				return;
			}

			if (ret == 0)
			{
				/* todo close this connection */
				GAME_LOG.debug("connection close in read header[%d]\n", self->fd);
				self->destroy();
				return;
			}

			if (self->_header[0] == '<'
				&& self->_header[1] == 'p'
				&& self->_header[2] == 'o'
				&& self->_header[3] == 'l')
			{
				self->_state = PARSE_CROSS_DOMAIN;
				self->_cur_head_len = 0;
				self->_cur_policy_len = 4;
				return;
			}

			self->_cur_head_len += ret;

			if (self->_cur_head_len == sizeof(self->_header))
			{
				/*
				if (self->_header_p->sig[0] != 0xF1 || self->_header_p->sig[1] != 0xFA)
				{
				GAME_LOG.error("sig err[%02X %02X]\n",
				self->_header_p->sig[0], self->_header_p->sig[1]);
				return;
				}
				*/

				if (self->_header_p->length > MAX_BUF_LEN
					|| self->_header_p->length == 0)
				{
					GAME_LOG.error("fd[%d] recieve an error len package\n");
					self->destroy();
					return;
				}

				/* a complete header */
				self->_state = PARSE_BODY;
				self->_cur_head_len = 0;
				self->body.clear();

				/* check header flag */
				/* ... */
				//GAME_LOG.debug("check header ok\n");
			}
		}
		else if (self->_state == PARSE_BODY)
		{
			ret = read(self->fd, recv_buf, self->_header_p->length - self->body.length());
			if (ret <= 0)
			{
				if (errno == EAGAIN || errno == EINPROGRESS || errno == EINTR)
				{
					/* redo */
					GAME_LOG.debug("read body failed[%s]\n", strerror(errno));
					return;
				}
				GAME_LOG.error("unknow err in read body[%d]\n", self->fd);
				self->destroy();
				return;
			}

			if (ret == 0)
			{
				/* todo close this connection */
				GAME_LOG.debug("connection close in read body[%d]\n", self->fd);
				self->destroy();
				return;
			}

			recv_buf[ret] = '\0';
			self->body.append(recv_buf);

			if (self->body.length() == self->_header_p->length)
			{
				/* complete body */
				self->_state = PARSE_HEADER;

				/* process */

				int ret = self->handle_data();

				if (ret < 0)
				{
					GAME_LOG.error("dispatch err\n");
					self->pre_destroy();
					return;
				}

				if (self->is_err == 1)
				{
					GAME_LOG.error("client is err\n");
					self->destroy();
				}
			}
		}
		else if (self->_state == PARSE_CROSS_DOMAIN)
		{
			ret = read(self->fd, recv_buf,
				sizeof(policy_file) - self->_cur_policy_len);
			if (ret <= 0)
			{
				if (errno == EAGAIN || errno == EINPROGRESS || errno == EINTR)
				{
					/* redo */
					GAME_LOG.debug("read body failed[%s]\n", strerror(errno));
					return;
				}
				GAME_LOG.error("unknow err in read body[%d]\n", self->fd);
				self->destroy();
				return;
			}
			if (ret == 0)
			{
				/* todo close this connection */
				GAME_LOG.debug("connection close in read body[%d]\n", self->fd);
				self->destroy();
				return;
			}

			self->_cur_policy_len += ret;
			if (self->_cur_policy_len == sizeof(policy_file))
			{
				/* completed */
				self->_state = PARSE_HEADER;
				self->_cur_policy_len = 0;
				self->send(policy_cross_domain, sizeof(policy_cross_domain));
				return;
			}
		}
	}
	static void write_cb(struct ev_loop *loop, struct ev_io *w, int revents) 
	{
		TcpClient *self = (TcpClient*)w->data;

		if (self->_write_q.empty())
		{
			//GAME_LOG.debug("stop write event\n");
			ev_io_stop(EV_A_ w);
			if (self->is_close == 1)
			{
				self->destroy();
				return;
			}
			return;
		}

		Buffer* buffer = self->_write_q.front();

		ssize_t written = write(self->fd, buffer->dpos(), buffer->nbytes());
		if (written < 0)
		{
			if (errno == EAGAIN || errno == EINPROGRESS || errno == EINTR)
			{
				/* redo */
				GAME_LOG.debug("write failed[%s]\n", strerror(errno));
				return;
			}
			/* todo close this client */
			GAME_LOG.error("unknow err in written [%d]\n", self->fd);
			self->destroy();
			return;
		}
		//GAME_LOG.debug("send data\n");

		buffer->pos += written;
		if (buffer->nbytes() == 0)
		{
			self->_write_q.pop_front();
			delete buffer;
		}
	}
	int send(const char *buf, unsigned int len)
	{
		if (fd > 0)
		{
			if (_write_q.empty())
			{
				_ev_write.data = this;
				ev_io_start(YXJMJ_CONF._loop, &_ev_write);
				//GAME_LOG.debug("start write event\n");
			}
			_write_q.push_back(new Buffer(buf, len));
			//GAME_LOG.debug("write data\n", buf);
			return 0;
		}
		return -1;
	}
	int send(const std::string &res)
	{
		return send(res.c_str(), res.length());
	}
	unsigned int safe_writen(const char *buf, unsigned int len)
	{
		size_t nleft;
		ssize_t nwritten;
		const char *ptr;

		ptr = (char*)buf;
		nleft = len;

		while (nleft > 0) {
			nwritten = write(fd, ptr, nleft);
			if (nwritten <= 0) {
				if (errno == EINTR)
					nwritten = 0;
				else {
					is_err = 1;
					return -1;
				}
			}
			nleft -= nwritten;
			ptr += nwritten;
		}

		return len;
	}
};

